package com.sxf.core.advice;


import com.sxf.common.enums.SystemCommonCodeEnum;
import com.sxf.common.exceptions.BaseException;
import com.sxf.common.exceptions.BaseRuntimeException;
import com.sxf.common.exceptions.IllegalParameterException;
import com.sxf.common.param.ResultInfo;
import com.sxf.consts.Consts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.MultipartException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 
 * @description: 异常拦截器
 * @author: zhangyb@ccccit.com.cn
 * @date: 2021年1月5日 下午4:23:58
 */
@RestControllerAdvice
public class ExceptionHandleAdvice {

	private final static Logger LOGGER = LoggerFactory.getLogger(ExceptionHandleAdvice.class);

	/**
	 * 访问接口参数不全
	 * 
	 * @param request
	 * @param pe
	 * @return
	 */
	@ExceptionHandler(MissingServletRequestParameterException.class)
	public ResultInfo<?> missingServletRequestParameterException(HttpServletRequest request, MissingServletRequestParameterException pe) {
		LOGGER.error("参数校验异常:" + request.getRequestURI(), pe);
		return ResultInfo.errorMessage(SystemCommonCodeEnum.ILLEGL_PARAM_ERR.getCode(),
				"该请求路径：" + request.getRequestURI() + " 下缺失请求参数：" + pe.getParameterName());
	}

	/**
	 * Bean 校验异常
	 * 
	 * @param request
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResultInfo<?> methodArgumentNotValidExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException exception) {
		LOGGER.error("参数校验异常:" + request.getRequestURI(), exception);
		BindingResult bindingResult = exception.getBindingResult();
		List<ObjectError> allErrors = bindingResult.getAllErrors();
		StringBuffer stringBuffer = new StringBuffer();
		if (allErrors != null) {
			int errorCount = allErrors.size();
			for (int i = 0; i < errorCount; i++) {
				ObjectError objectError = allErrors.get(i);
				if (objectError instanceof FieldError) {
					FieldError fieldError = (FieldError) objectError;
					stringBuffer.append(fieldError.getField() + ":" + fieldError.getDefaultMessage());
				} else {
					stringBuffer.append(objectError.getDefaultMessage());
				}
				if (i != errorCount - 1) {
					stringBuffer.append(" , ");
				}
			}
		}
		return ResultInfo.errorMessage(SystemCommonCodeEnum.ILLEGL_PARAM_ERR.getCode(), "参数输入有误:" + stringBuffer);
	}

	/**
	 * Bean 自定义校验异常
	 * 
	 * @param request
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(IllegalParameterException.class)
	public ResultInfo<?> customMethodArgumentNotValidExceptionHandler(HttpServletRequest request, IllegalParameterException exception) {
		LOGGER.error("参数校验异常:" + request.getRequestURI(), exception);
		return ResultInfo.errorMessage(SystemCommonCodeEnum.ILLEGL_PARAM_ERR.getCode(), "参数输入有误:" + exception.getMessage());
	}

	/**
	 * 参数绑定异常
	 * @param request
	 * @param be
	 * @return
	 */
	@ExceptionHandler(BindException.class)
	public ResultInfo<?> validationExceptionHandler(HttpServletRequest request, BindException be) {
		LOGGER.info("参数校验异常:" + request.getRequestURI(), be);
		BindingResult bindingResult = be.getBindingResult();
		String errorMesssage = "";
		for (FieldError fieldError : bindingResult.getFieldErrors()) {
			errorMesssage += fieldError.getDefaultMessage() + "!";
		}
		return ResultInfo.errorMessage(SystemCommonCodeEnum.ILLEGL_PARAM_ERR.getCode(), "参数输入有误:" + errorMesssage);
	}

	/**
	 * 方法参数校验异常如实体类中的@Size注解配置和数据库中该字段的长度不统一等问题
	 * @param request
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(ConstraintViolationException.class)
	public ResultInfo<?> constraintViolationExceptionHandler(HttpServletRequest request, ConstraintViolationException ex) {
		LOGGER.info("参数校验异常:" + request.getRequestURI(), ex);
		Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
		Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
		List<String> msgList = new ArrayList<>();
		while (iterator.hasNext()) {
			ConstraintViolation<?> cvl = iterator.next();
			msgList.add(cvl.getPropertyPath() + "," + cvl.getMessage());
		}
		return ResultInfo.errorMessage(SystemCommonCodeEnum.ILLEGL_PARAM_ERR.getCode(), "参数输入有误:" + String.join(",", msgList));
	}

	/**
	 * BaseRuntimeException异常处理
	 * @param baseRuntimeException
	 * @return
	 */
	@ExceptionHandler(BaseRuntimeException.class)
	public ResultInfo<?> handle(BaseRuntimeException baseRuntimeException) {
		LOGGER.error("controller 执行异常.", baseRuntimeException);
		return ResultInfo.errorMessage(baseRuntimeException.getErrorCode(), baseRuntimeException.getMessage());
	}

	/**
	 * BaseException异常处理
	 * @param baseException
	 * @return
	 */
	@ExceptionHandler(BaseException.class)
	public ResultInfo<?> handle(BaseException baseException) {
		LOGGER.error("controller 执行异常.", baseException);
		return ResultInfo.errorMessage(baseException.getErrorCode(), baseException.getMessage());
	}

	/**
	 * Exception异常处理
	 * @param e
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public ResultInfo<?> handler(Exception e) {
		// 把漏网的异常信息记入日志
		LOGGER.error("Exception:{}", e.getMessage(), e);
		return ResultInfo.errorMessage("服务器内部错误");
	}

	@Value("${spring.servlet.multipart.max-file-size:" + Consts.DEFAULT_MAX_FILE_SIZE + "}")
	private String maxFileSize;

	/**
	 * 附件大小异常处理
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(MultipartException.class)
	public ResultInfo<?> multipartExceptionHandler(MultipartException exception) {
		LOGGER.error("上传文件异常", exception);
		return ResultInfo.errorMessage(SystemCommonCodeEnum.ILLEGL_PARAM_ERR.getCode(), "文件大小超出限制，最大：" + maxFileSize);
	}

	/**
	 * 异步请求时异常处理
	 * @param exception
	 * @return
	 */
	@ExceptionHandler(AsyncRequestTimeoutException.class)
	public ResultInfo<?> asyncRequestTimeoutExceptionHandler(Exception exception) {
		LOGGER.error("内部请求超时", exception);
		return ResultInfo.errorMessage(SystemCommonCodeEnum.RPC_ERR.getCode(), "内部请求超时");
	}

}
